#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include "sem.hpp"

const int g_default_num = 5; // 队列的大小

template<class T>
class RingQueue
{
public:
    RingQueue(int default_num = g_default_num)
        :_ring_queue(default_num)
        ,_num(default_num)
        ,c_step(0)
        ,p_step(0)
        ,space_sem(default_num)  // 开始时,生产空间是满的
        ,data_sem(0)  // 开始时,消费的数据是空的
    {
        pthread_mutex_init(&clock, nullptr);
        pthread_mutex_init(&plock, nullptr);
    }

    void push(const T& in) // 生产者
    {
        // 先申请信号量,再加锁
        space_sem.P(); // 申请信号量
        pthread_mutex_lock(&plock); // 生产者 上锁

        _ring_queue[p_step++] = in; // 生产数据
        p_step %= _num;

        pthread_mutex_unlock(&plock); // 生产者 解锁
        data_sem.V(); // 有数据了, 进行V操作
    }

    void pop(T& out) // 消费者
    {
        data_sem.P(); // 申请信号量
        pthread_mutex_lock(&clock); // 消费者 上锁

        out = _ring_queue[c_step++]; // 拿走数据
        c_step %= _num;

        pthread_mutex_unlock(&clock); // 消费者 解锁
        space_sem.V(); // 拿走数据了, 进行V操作
    }

    ~RingQueue()
    {
        pthread_mutex_destroy(&clock);
        pthread_mutex_destroy(&plock);
    }
private:
    std::vector<T> _ring_queue;
    int _num;
    int c_step; // 消费下标
    int p_step; // 生产下标
    Sem  space_sem;  // 生产空间
    Sem data_sem;  // 消费空间
    pthread_mutex_t clock;
    pthread_mutex_t plock;
};
